/* static members */
/* caesardict & dictlen only used for cryptletter */
static char*
caesardict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static int dictlen = 52;

/* method definitions */
static int isasciicharacter(char c);
static int isasciiletter(char c);
static int wrap(int n, int lb, int ub);
static char cryptchar(char c, char k, int mode);
static char cryptletter(char c, char k, int mode);

/* method implementations */
int isasciicharacter(char c) {
    // we have to check ascii characters, up to 32 ascii is full of
    // important control sequences, we have to ignore those
    if (c < 32 || c > 126) return 0;
    return 1;
}

// checks whether character is a valid ascii letter
int isasciiletter(char c) {
    // if c is not out of bounds of all ascii upper case characters
    //     and not out of bounds of all ascii lower case characters
    if (!(c >= 65 && c <= 90) && !(c >= 97 && c <= 122)) return 0;
    return 1;
}

// wraps n around a lower bound 'lb' and an upper bound 'ub' (inclusive)
int wrap(int n, int lb, int ub) {
    // if n is bigger than our ub, take the lb as the base and add
    // the the modulo of n and (ub + 1), which is the remainder of
    // n / (ub + 1)
    if (n > ub) return lb + (n % (ub + 1));
    // if n is smaller than our lb, take the (ub + 1) as the base
    // and subtract the diff between lb and n (not just n, as the
    // highest n value would yield the lowest ub - n value
    if (n < lb) return (ub + 1) - (lb - n);
    return n;
}

// this method only, like caesar intended, handles letters!
// usable for ascii ranges [65;90] and [97;122]
char cryptletter(char c, char k, int mode) {
    // in case we're not dealing with ascii, simply do not care
    if (!isasciicharacter(c)) return c;

    // skip any non ASCII letter (upper or lowercase) chars or keys
    //if (!isasciiletter(c) || !isasciiletter(k)) return cryptchar(c, k, mode);
    if (!isasciiletter(c) || !isasciiletter(k)) return c;

    /* 65 is 'A' & 97 is 'a' */
    /* offset to 'a' for current k */
    int kofftoa = 0;
    if (k >= 97) kofftoa = (k - 97) + (dictlen/2);
    if (k <= 90) kofftoa = (k - 65);
    /* offset to 'a' for current c */
    int cofftoa = 0;
    if (c >= 97) cofftoa = (c - 97) + (dictlen/2);
    if (c <= 90) cofftoa = (c - 65);

    // use encrypt/decrypt modifier (1/-1)
    kofftoa *= (mode ? 1 : -1);

    /* finally simply add key offset to char offset and wrap around dict */
    int w = wrap((cofftoa + kofftoa), 0, dictlen - 1);
    /* the dict dictates the result */
    c = caesardict[w];

    return c;
}

// this method should only be used called by cryptletter!
char cryptchar(char c, char k, int mode) {
    // in case we're not dealing with ascii, simply do not care
    if (!isasciicharacter(c)) return c;

    // same as in cryptletter, only with ' ' (the first non control character)
    // as the base value
    int off = k - 32;
    off *= (mode ? 1 : -1);
    c = wrap((int) (c + off), 32, 126);
    return c;
}
